package com.zhanghp.class013;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

/**
 * @author zhanghp
 * @since 2024/2/27 13:59
 */
public class StackQueueAndCircularQueue {

    // 直接用java内部的实现
    // 其实内部就是双向链表，常数操作慢
    public static class Queue1 {
        // java中的双向链表LinkedList
        // 单向链表就足够了
        public Queue<Integer> queue = new LinkedList<Integer>();

        public boolean isEmpty() {
            return queue.isEmpty();
        }

        // 入队，加到尾巴
        public void offer(int num) {
            queue.offer(num);
        }

        // 出队，从头拿
        public int poll() {
            return queue.poll();
        }

        // 查看队头元素
        public int peek() {
            return queue.peek();
        }

        public int size() {
            return queue.size();
        }
    }

    // 实际刷题时更常见的写法，常数时间好
    // 如果可以确定加入操作的总次数不超过n，那么可以用
    // 一般笔试、面试都会有一个明确数据量，所以这是最常用的方式
    public static class Queue2 {
        public int[] queue;
        public int l, r;

        // 加入操作的总次数上限是多少，一定要明确
        public Queue2(int size) {
            queue = new int[size];
            l = r = 0;
        }

        // 调用任何方法之前，先调用这个方法来判断队列内是否有东西
        public boolean isEmpty() {
            return l == r;
        }

        public void offer(int val) {
            queue[r++] = val;
        }

        public int poll() {
            return queue[l++];
        }

        public int peek() {
            return queue[l];
        }

        public int size() {
            return r - l;
        }

        // ?
        // l...r-1 r
        // [l..r)
        public int head() {
            return queue[l];
        }

        public int tail() {
            return queue[r - 1];
        }

    }

    // 直接用java内部的实现
    // 其实就是动态数组，不过常数时间并不好
    public static class Stack1 {
        public Stack<Integer> stack = new Stack<>();

        // 调用任何方法之前，先调用这个方法来判断栈内是否有东西
        public boolean isEmpty() {
            return stack.isEmpty();
        }

        public void push(int num) {
            stack.push(num);
        }

        public int pop() {
            return stack.pop();
        }

        public int peek() {
            return stack.peek();
        }

        public int size() {
            return stack.size();
        }

    }

    // 实际刷题时更常见的写法，常数时间好
    // 如果可以保证同时在栈里的元素个数不会超过n，那么可以用
    // 也就是发生弹出操作之后，空间可以复用
    // 一般笔试、面试都会有一个明确数据量，所以这是最常用的方式
    public static class Stack2 {
        public int[] stack;
        public int size;

        public Stack2(int num) {
            stack = new int[num];
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public void push(int val) {
            stack[size++] = val;
        }

        public int poll() {
            return stack[--size];
        }

        public int peek() {
            return stack[size - 1];
        }


        public int size() {
            return size;
        }

    }

    // 设计循环队列
    // 测试链接 : https://leetcode.cn/problems/design-circular-queue/
    static class MyCircularQueue {

        public int[] circularQueue;

        public int l, r, limit, size;

        public MyCircularQueue(int k) {
            circularQueue = new int[k];
            l  = r = size = 0;
            limit = k;
        }

        // 如果队列满了，什么也不做，返回false
        // 如果队列没满，加入value，返回true
        public boolean enQueue(int value) {
            if (isFull()) {
                return false;
            }
            circularQueue[r] = value;
            r = r == limit - 1 ? 0 : (r + 1);
            size++;
            return true;
        }

        // 如果队列空了，什么也不做，返回false
        // 如果队列没空，弹出头部的数字，返回true
        public boolean deQueue() {
            if (isEmpty()) {
                return false;
            }
            l = l == limit - 1 ? 0 : (l + 1);
            size --;
            return true;
        }

        // 返回队列头部的数字（不弹出），如果没有数返回-1
        public int Front() {
            if (isEmpty()) {
                return -1;
            }
            return circularQueue[l];
        }

        // 返回队列尾部的数字（不弹出），如果没有数返回-1
        public int Rear() {
            if (isEmpty()) {
                return -1;
            }else {
                // 添加元素后，r会像后走一位，所以需要处理
                return circularQueue[r == 0 ? (limit - 1) : (r - 1)];
            }
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public boolean isFull() {
            return size == limit;
        }
    }
}
